package client;

import info.clearthought.layout.TableLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import utils.Settings;
import utils.TableList;
import utils.TableListGUI;
import utils.User;
import utils.UserListGUI;
import utils.Utils;

import commands.CommandBringStatistics;
import commands.CommandChatMessage;
import commands.CommandJoinTable;
import commands.CommandJoinTableToOwner;
import commands.CommandUserExit;
import components.ChatPanel;
import components.LogoPanel;
import components.RPSButton;
import components.RPSPanel;

/**
 * The panel where the user sees the current games and current users. He can join or start a game and 
 * send chat messages
 */
public class ClientPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	public static final int SPACING = 5;
	
	private IGameConnection conn;
	
	private RPSButton newGameButton, joinButton;
	private RPSButton rulesButton;
	private RPSButton myStatisticsButton;
	private RPSButton exitButton;
	private TableList tableList;
	private UserListGUI userListGUI;
	private TableListGUI tableListGUI;
	private ChatPanel chatPanel;
	
	private String username;
	
	
	public ClientPanel (IGameConnection conn) {
	
		this.conn = conn;
		username = conn.getUsername();
		
		tableList = this.conn.getTableList();
        
        setUpGUI();
        addListeners();
	}
	
	private void setUpGUI () {
		
	    // Create Tablelayout variables
	    double pref = TableLayout.PREFERRED;
	    double fill = TableLayout.FILL;
	    double [][] sizes;

		// Create user and table panels
	    conn.getClientFrame().setTitle(username);
	    
	    //Create Logo Panel
	    sizes = new double [][] {{fill}, {pref, fill}};
	    setLayout(new TableLayout(sizes));
	    
	    LogoPanel logoPanel = new LogoPanel();
	    add(logoPanel, "0, 0");   
	   
	    //Create LowerPanel
	    sizes = new double [][] {{0.3,0.7}, {fill}};
	    JPanel lowerPanel = new JPanel(new TableLayout(sizes));
	    
	    // Create LowerLeftPanel
	    sizes = new double[][] { {fill}, {fill} };
	    RPSPanel lowerLeftPanel = new RPSPanel(new TableLayout(sizes));
	    lowerLeftPanel.setBorder("Current Connected Users");

	    userListGUI = new UserListGUI(conn.getGameInfo().getUserList());
	    lowerLeftPanel.add(userListGUI, "0, 0");
	    
	    lowerPanel.add(lowerLeftPanel, "0, 0");
	    
	    //Create LowerRightPanel
	    sizes = new double [][] {{fill}, {pref, SPACING, fill, SPACING, fill }};
	    RPSPanel lowerRightPanel = new RPSPanel(new TableLayout(sizes));
	    lowerRightPanel.setBorder("Current Games Being Played");
	    
	    //Create ButtonsPanel
	    sizes = new double [][] {{pref, SPACING, pref, SPACING * 5, pref, SPACING, pref, fill, pref}, {pref}};
	    RPSPanel buttonsPanel = new RPSPanel(new TableLayout(sizes));
	    newGameButton = new RPSButton("New Game");
	    joinButton = new RPSButton("Join");
	    rulesButton = new RPSButton("Rules");
	    myStatisticsButton = new RPSButton("My Statistics");
	    exitButton = new RPSButton("Exit");
	    
	    buttonsPanel.add(newGameButton,"0, 0");
	    buttonsPanel.add(joinButton, "2, 0");
	    buttonsPanel.add(rulesButton, "4, 0");
	    buttonsPanel.add(myStatisticsButton, "6, 0");
	    buttonsPanel.add(exitButton, "8, 0");
	    
	    lowerRightPanel.add(buttonsPanel, "0, 0");
	    
	    //add table list
	    tableListGUI = new TableListGUI(conn.getGameInfo().getTableList());
	    lowerRightPanel.add(tableListGUI, "0, 2");
	    
	    //add chat
	    chatPanel = new ChatPanel(conn, 0);
	    lowerRightPanel.add(chatPanel, "0, 4");
	    
	    lowerPanel.add(lowerRightPanel, "1, 0");
	    add(lowerPanel, "0, 1");
	    
	    updateButtonStates();
	    
	    invalidate();
		validate();
	}
	
	private void updateButtonStates () {
		// If (and only if) there is a selected table, enable the Join button
		joinButton.setEnabled(! tableListGUI.isSelectionEmpty());
	}

	private void addListeners () {
		
		// Declare listener for the new game button.
	    this.newGameButton.addActionListener (
			new ActionListener () {
				public void actionPerformed (ActionEvent e) {
					if (Utils.isUserPlayingInATable(tableList, username) == false)
					{
						// Open the New Game Settings dialog
						NewGameSettingsDialog newGameSettingsDialog = new NewGameSettingsDialog(conn.getClientFrame(), conn);			
						newGameSettingsDialog.setVisible(true);
						newGameSettingsDialog.validate();
					}
					else
					{
						String message = "You are already an active player in a table and therefore cannot create another.";
						JOptionPane.showMessageDialog(conn.getClientFrame(), message, "Creating A Table", JOptionPane.INFORMATION_MESSAGE);
					}
				}
			}
		);

		// Listener for the join button
	    this.joinButton.addActionListener (
			new ActionListener() {
				public void actionPerformed (ActionEvent e) {
					doJoinButtonAction();
				}
			}
		);

		// Listener for the rules button
		this.rulesButton.addActionListener (
			new ActionListener() {
				public void actionPerformed (ActionEvent e) {
					String message = "This is a combination of an RPS game with a board game." + Settings.NEW_LINE +
									 "Each player first assigns a soldier to be the flag and a soldier to be the trap (To be explained soon)." + Settings.NEW_LINE +
									 "In each turn, the current player chooses a soldier to move with (Except for the flag and the trap)" + Settings.NEW_LINE +
									 "and can move one cell (Not including diagonals)." + Settings.NEW_LINE +
									 "When a battle occurs, the winner is not necessarily the attacker (As in chess, for example), but it is decided" + Settings.NEW_LINE +
									 "by the regular rules of RPS, unless a player attacks the trap, and then he will always lose." + Settings.NEW_LINE +
									 "The game ends when one of the players attacks the flag of the other one, and thus wins the game.";
					JOptionPane.showMessageDialog(conn.getClientFrame(), message, "Game Rules" , JOptionPane.INFORMATION_MESSAGE);
				}
			}
		);

		myStatisticsButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				doMyStatisticsAction();
				
			}
		});
		
		exitButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e)
			{
				closeClient();
			}
		});
		
		// Listener on the table list
	    this.tableListGUI.addListSelectionListener (
			new ListSelectionListener () {
				public void valueChanged(ListSelectionEvent listselectionevent) {
					updateButtonStates ();
				}
			}
		);
		
		// Double-click listener on the table list
		this.tableListGUI.addMouseListener (
			new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					// If this is a double click and the joinButton is enabled...
					if ((e.getClickCount() == 2) && joinButton.isEnabled()) {
						// ... then do the same action as clicking the join button
						doJoinButtonAction();
					}
				}
			}
		);
	}
	
	protected void doJoinButtonAction()
	{
		// Retrieve the current selected table number
		int selectedTableNum = tableListGUI.getSelectedTableNum ();
		
		if (tableList.getTable(selectedTableNum).containsPlayer(username))
		{
			String message = "You are already in this table.";
			JOptionPane.showMessageDialog(conn.getClientFrame(), message, "Joining A Table", JOptionPane.INFORMATION_MESSAGE);
			
			return;
		}
		
		// Create join table request and send to server.
		CommandJoinTable joinTable = new CommandJoinTable (username, selectedTableNum);
		conn.send (joinTable);
		
		tableListGUI.getSelectedTable().addPlayer(new User(username));
		tableList.refreshObservers();
		
		Socket clientSocket = null;
		
		try {
			String ipOwner = tableList.getTable(selectedTableNum).getOwnerIP();
			clientSocket = new Socket(ipOwner, Settings.P2P_PORT);
			
			ConnectionThreadTableClientToOwner connectionThreadTableClientToOwner = new ConnectionThreadTableClientToOwner (clientSocket, username, conn);
			connectionThreadTableClientToOwner.start();
			connectionThreadTableClientToOwner.send(new CommandJoinTableToOwner(username));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (ConnectException e) {
			String message = "Cannot join this table. The owner probably got disconnected unexpectedly.";
			JOptionPane.showMessageDialog(getParent(), message, "Cannot Join Table", JOptionPane.INFORMATION_MESSAGE);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	protected void doMyStatisticsAction()
	{
		// Create bring statistics request and send to server.
		CommandBringStatistics bringStatistics = new CommandBringStatistics (conn.getUsername());
		conn.send(bringStatistics);
	}
	
	public void receiveGameChat (CommandChatMessage chatMessage) {
	    String messageText = chatMessage.getChat();
	    String usernameFrom = chatMessage.getUsername();
	    chatPanel.receiveMessage (usernameFrom, messageText);
	}
	
	public void closeClient () {
		String message = "Are you sure you want to exit ?";
		if (Utils.isUserPlayingInATable(tableList, username))
		{
			message += Settings.NEW_LINE + "(If you are currently playing a game, you will automatically lose it)";
		}
		int optionChosen = JOptionPane.DEFAULT_OPTION;
		
		optionChosen = JOptionPane.showConfirmDialog(conn.getClientFrame(), message, "Leaving so soon ?",
													 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
		
		// Check if the user hasn't canceled the InputDialog, meaning there is an answer
		if (optionChosen == JOptionPane.YES_OPTION)
		{
			// Notify the server that the user is exiting
			CommandUserExit commandUserExit = new CommandUserExit(username);
			conn.send(commandUserExit);
			
			// Exit the application
			System.exit(0);
		}
	}
}